package com.northpool.operator.pub.service;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.northpool.operator.annotation.InputParam;
import com.northpool.operator.annotation.InputParams;
import com.northpool.operator.annotation.OperatorInfo;
import com.northpool.operator.annotation.OutputParam;
import com.northpool.operator.annotation.OutputParams;
import com.northpool.operator.common.algorithm.OperatorTestBuild;
import com.northpool.operator.common.algorithm.TestParamBuilder;
import com.northpool.operator.common.database.DatabaseUtils;
import com.northpool.operator.common.stpclient.StpClientOperatorBase;
import com.northpool.operator.param.ParamTool;
import com.northpool.operator.pub.service.request.CreateServerRequest;
import com.northpool.operator.pub.service.request.MapserviceEntity;
import com.northpool.operator.pub.service.request.RegisterDataRequest;
import com.northpool.operator.pub.service.request.SaveStyleRequest;
import com.northpool.operator.pub.service.request.StyleEntity;
import com.northpool.operator.type.TypeEnum;
import com.northpool.resources.datasource.IDataSource;
import com.northpool.resources.datatable.FieldEncoder;
import com.northpool.resources.datatable.ITable;
import com.northpool.resources.datatable.SpatialInfo;
import com.northpool.spatial.Constants;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

@OperatorInfo(
        ref = "PubVecServer",
        name = "发布矢量服务",
        version = "1.0.0"
)
@OutputParams({
        @OutputParam(ref = "serviceId", name = "服务名称", type = TypeEnum.STRING),
        @OutputParam(ref = "styleId", name = "样式名称", type = TypeEnum.STRING),
        @OutputParam(ref = "vmap_url", name = "矢量底图地址", type = TypeEnum.STRING),
        @OutputParam(ref = "preview_url", name = "服务预览地址", type = TypeEnum.STRING),
        @OutputParam(ref = "editor_url", name = "样式编辑地址", type = TypeEnum.STRING),
})
public class PubVecServer extends StpClientOperatorBase {
    final static Logger logger = LoggerFactory.getLogger(PubVecServer.class);
    protected String TOKEN = "e84666b76f5203b4c03f84454f95962f";
    protected String SPATIAL_SERVER_URL = "%s/apis/datasource/list?typeCode=0101&page=1&rows=1000&name=&orderBy=createTime+desc&selectStyle=exact";
    protected String LAYER_TEMPLATE_URL = "%s/mapserver/vmap/%s/getMap?styleId=%s&x={x}&y={y}&l={z}&tilesize=512";
    protected String SERVER_PREVIEW_URL = "%s/service-web/#/services/map-preview/%s/3/default?token=%s";
    protected String SERVER_EDIT_STYLE_URL = "%s/service-web/#/map-editor/%s/%s/update?token=%s";

    @InputParams({
            @InputParam(ref = "serviceUrl", name = "空服URL地址", desc = "空服服务根路径，如: http://192.168.164.45:8021", type = TypeEnum.STRING, required = true),
            @InputParam(ref = "dataCenterId", name = "数据源ID", type = TypeEnum.STRING, required = true),
            @InputParam(ref = "tableName", name = "数据表名称", desc = "数据表名称，如: public.table", type = TypeEnum.STRING, required = true),
            @InputParam(ref = "serviceId", name = "服务ID", desc = "服务名称", type = TypeEnum.STRING, required = true),
            @InputParam(ref = "styleId", name = "样式ID", desc = "样式名称", type = TypeEnum.STRING, value = "custom_style"),
            @InputParam(ref = "fields", name = "字段列表", desc = "数据字段列表，多个逗号隔开，如: f_id,dlmc,dlbm", type = TypeEnum.STRING),
            @InputParam(ref = "styleJson", name = "样式JSON", type = TypeEnum.STRING, required = false, value = "{\"strokeWidth\":\"1\",\"strokeColor\":\"#da0012\",\"fillOpacity\":\"1\",\"fillColor\":\"#da0012\",\"strokeOpacity\":\"1\"}"),
            @InputParam(ref = "createSpatialIndex", name = "创建空间索引",value = "false", desc = "存在空间索引的数据才能发布服务，在发布服务前生成空间索引", type = TypeEnum.BOOL, required = false),
            @InputParam(ref = "token", name = "用户TOKEN", desc = "平台用户TOKEN", type = TypeEnum.STRING, required = false, value = "e84666b76f5203b4c03f84454f95962f")
    })
    public PubVecServer() {
    }

    public static void main(String[] args) throws Exception {
        OperatorTestBuild.execute(PubVecServer.class,
                TestParamBuilder.aBuilder()
                        .param("serviceUrl", "http://192.168.164.45")
                        .param("dataCenterId", "402881ac934fb41701934fc212af000b")
                        .param("tableName", "result.相交结果_耕地流入流出")
                        .param("serviceId", "reuslt_0416_2")
                        .param("styleId", "custom_style")
                        .param("token", "28d129d0c3e48b340ed68eb3275927f1")
                        .build());
    }

    protected String dataCenterId;
    protected String dataSourceId = null;
    protected String serviceUrl;
    protected String serviceId;

    protected String tableName;
    protected String styleId;
    protected String styleJson;
    protected String fields;
    protected Boolean createSpatialIndex;

    static Map<String, String> styleMap = new HashMap<>();

    static {
        styleMap.put("strokeWidth", "1");
        styleMap.put("strokeColor", "#4C5250");
        styleMap.put("fillOpacity", "1");
        styleMap.put("fillColor", "#4C5250");
        styleMap.put("strokeOpacity", "1");
    }

    public void execute(ParamTool params) throws Exception {
        // 1.发布数据服务
        serviceUrl = params.getStr("serviceUrl");
        dataCenterId = params.getStr("dataCenterId");
        tableName = params.getStr("tableName");
        serviceId = params.getStr("serviceId");
        createSpatialIndex = params.getBool("createSpatialIndex");
        styleId = params.getStr("styleId");
        fields = params.getStr("fields");
        styleJson = params.getStr("styleJson");
        TOKEN = params.getStr("token");

        String dataSourceListUrl = String.format(SPATIAL_SERVER_URL, serviceUrl);
        HttpResponse execute = HttpUtil.createGet(dataSourceListUrl)
                .header("token", TOKEN).execute();

        String body = execute.body();
        JSONObject jsonObject = JSONObject.parseObject(body);
        logger.info(jsonObject.toJSONString());
        JSONObject dataObj = jsonObject.getJSONObject("data");
        JSONArray records = dataObj.getJSONArray("records");
        for (Object record : records) {
            JSONObject obj = (JSONObject) record;
            String centerId = obj.getString("dataCenterId");
            if(centerId == null) continue;
            if (centerId.equals(dataCenterId)) {
                dataSourceId = obj.getString("id");
                break;
            }
        }
        if (dataSourceId == null) {
           throw new RuntimeException("空服数据源ID未找到关联的数据中心数据源");
        }

        String keyFieldName;
        String geomFieldName;

        // 初始化样式
        JSONObject styleObj = JSONObject.parseObject(styleJson);
        for (String key : styleObj.keySet()) {
            if (styleMap.containsKey(key)) {
                styleMap.put(key, styleObj.getString(key));
            }
        }
        String dataSourceJson = DatabaseUtils.getDataSourceJson(this.dataCenterId);
        IDataSource dataSource = IDataSource.fromJSON(dataSourceJson);

        String[] fieldsArr = null;
        if (StringUtils.isNotBlank(fields)) {
            fieldsArr = fields.split(",");
        }

        ITable table = dataSource.getTable(tableName, fieldsArr, FieldEncoder.ORIGIN_FIELD_ENCODER);
        if (ArrayUtils.isEmpty(fieldsArr)) {
            fields = String.join(",", table.getFields());
        }
        if (createSpatialIndex) {
            table.getTableOperator().createIndex(false, table.spatialInfo().getSpatialField());
        }
        String idField = table.getIdField();
        if (StringUtils.isEmpty(idField)) {
            throw new RuntimeException("数据集主键缺失");
        }
        keyFieldName = idField;
        SpatialInfo spatialInfo = table.spatialInfo();
        geomFieldName = spatialInfo.getOriginFieldName();

        String dataId = this.publishDataService(serviceId, tableName, keyFieldName, geomFieldName, fields);

        // 2.发布地图服务
        CreateServerRequest createRequest = new CreateServerRequest();
        createRequest.setDataIds(ListUtil.of(dataId));

        Constants.GEO_TYPE geometryType = spatialInfo.getGeometryType();
        if (geometryType == null) {
            throw new RuntimeException("");
        }
        String style;
        if (geometryType.name().contains("POINT")) {
            style = this.getStyleTemplate("point");
        } else if (geometryType.name().contains("LINE")) {
            style = this.getStyleTemplate("line");
        } else if (geometryType.name().contains("POLYGON")) {
            style = this.getStyleTemplate("polygon");
        } else {
            throw new RuntimeException("空间字段几何类型" + geometryType + "不支持发服务");
        }
        style = style.replaceAll("\\{\\{name\\}\\}", serviceId);
        for (String key : styleMap.keySet()) {
            style = style.replaceAll("\\{\\{" + key + "\\}\\}", styleMap.get(key));
        }

        createRequest.setStyle(new StyleEntity(styleId, style));
        createRequest.setMapserviceEntity(new MapserviceEntity(serviceId, serviceId, spatialInfo.getSrid()));

        String resultCreate = HttpRequest.post(this.serviceUrl + "/apis/services/create")
                .header("token", TOKEN)
                .body(JSON.toJSONString(createRequest), "application/json")
                .execute().body();

        JSONObject resultCreteObj = JSONObject.parseObject(resultCreate);
        Integer createStatus = resultCreteObj.getInteger("status");
        if (!createStatus.equals(200)) {
            logger.error("矢量服务发布失败:{}", resultCreteObj.getString("msg"));
            throw new RuntimeException("矢量服务发布异常");
        }
        this.log("服务发布成功");
        String vecServiceIdReal = resultCreteObj.getJSONObject("data").getString("id");
        this.log(resultCreteObj.toJSONString());

        JSONObject serverData = resultCreteObj.getJSONObject("data");
        serviceId = serverData.getString("name"); // 服务名称可能已经变更，需要拿回传的服务名称

        // 3.保存样式
        logger.info("保存服务样式: {}", styleId);
        SaveStyleRequest styleRequest = new SaveStyleRequest();
        styleRequest.setServiceId(serviceId);
        styleRequest.setStyleId(styleId);
        styleRequest.setStyleName(styleId);
        styleRequest.setStyleContent(style);

        String resultStyle = HttpRequest.post(this.serviceUrl + "/apis/style/create")
                .header("token", TOKEN)
                .body(JSON.toJSONString(styleRequest), "application/json")
                .execute().body();

        logger.info(resultStyle);
        JSONObject resultStyleObj = JSONObject.parseObject(resultStyle);
        if (!resultStyleObj.getInteger("status").equals(200)) {
            logger.error("样式保存失败:{}", resultStyleObj.getString("msg"));
            throw new RuntimeException("样式保存异常");
        }

        this.result("serviceId", serviceId);
        this.result("styleId", styleId);

        String vmap_url = String.format(LAYER_TEMPLATE_URL, this.serviceUrl, this.serviceId, this.styleId);
        this.result("vmap_url", vmap_url);

        String preview_url = String.format(SERVER_PREVIEW_URL, this.serviceUrl, vecServiceIdReal, TOKEN);
        String editor_url = String.format(SERVER_EDIT_STYLE_URL, this.serviceUrl, vecServiceIdReal, styleId, TOKEN);

        this.result("preview_url", preview_url);
        this.result("editor_url", editor_url);
        String formatLayerUrl = String.format(LAYER_TEMPLATE_URL, this.serviceUrl, this.serviceId, this.styleId);
    }

    public String getStyleTemplate(String type) {
        return ResourceUtil.readUtf8Str("style/style_" + type + ".json");
    }

    public String publishDataService(String serviceName, String tableName, String keyFieldName, String geomFieldName, String fields) {
        logger.info("发布数据服务: {}", serviceName);
        RegisterDataRequest dataRequest = new RegisterDataRequest();
        dataRequest.setTableName(tableName);
        dataRequest.setKeyField(keyFieldName);
        dataRequest.setShapeField(geomFieldName);
        dataRequest.setFields(fields);
        dataRequest.setAlias(serviceName);
        dataRequest.setName(serviceName);
        dataRequest.setDatasourceId(this.dataSourceId);
        String dataRequestJson = "[" + JSON.toJSONString(dataRequest) + "]";
        String resultData = HttpRequest.post(serviceUrl + "/apis/dataservice/register")
                .header("token", TOKEN)
                .body(dataRequestJson, "application/json")
                .execute().body();
        logger.info(resultData);
        JSONObject resultDataObj = JSONObject.parseObject(resultData);
        Integer status = resultDataObj.getInteger("status");
        if (!status.equals(200)) {
            logger.error("数据服务发布失败:{}", resultDataObj.getString("msg"));
            error(dataRequestJson);
            throw new RuntimeException("数据服务发布异常: " + resultDataObj.getString("msg"));
        }
        JSONArray dataArr = resultDataObj.getJSONArray("data");
        return dataArr.getString(0);
    }
}
